﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using EpiNEXT.Core.Fields;

namespace EpiNEXT.WPF
{
    /// <summary>
    /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
    ///
    /// Step 1a) Using this custom control in a XAML file that exists in the current project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:EpiNEXT.WPF"
    ///
    ///
    /// Step 1b) Using this custom control in a XAML file that exists in a different project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:EpiNEXT.WPF;assembly=EpiNEXT.WPF"
    ///
    /// You will also need to add a project reference from the project where the XAML file lives
    /// to this project and Rebuild to avoid compilation errors:
    ///
    ///     Right click on the target project in the Solution Explorer and
    ///     "Add Reference"->"Projects"->[Browse to and select this project]
    ///
    ///
    /// Step 2)
    /// Go ahead and use your control in the XAML file.
    ///
    ///     <MyNamespace:FormDesignerCanvas/>
    ///
    /// </summary>
    public class FormDesignerCanvas : ItemsControl, IDisposable
    {
        #region Constructors
        static FormDesignerCanvas()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(FormDesignerCanvas), new FrameworkPropertyMetadata(typeof(FormDesignerCanvas)));
        }
        #endregion // Constructors

        #region Dependency Properties
        public static readonly DependencyProperty PageIdProperty = DependencyProperty.Register("PageId", typeof(Guid), typeof(FormDesignerCanvas));
        public Guid PageId
        {
            get
            {
                return (Guid)(this.GetValue(PageIdProperty));
            }
            set
            {
                this.SetValue(PageIdProperty, value);
            }
        }
        #endregion // Dependency Properties

        #region Properties
        private Dictionary<UIElement, IField> Controls { get; set; }
        private Dictionary<UIElement, ElementDesignBorder> Containers { get; set; }
        private double DragTop { get; set; }
        private double DragLeft { get; set; }
        #endregion // Properties

        #region Methods
        #region IDisposable Methods
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                var canvas = this.GetTemplateChild("canvasMain") as Canvas;
                if (canvas != null)
                {
                    //foreach (UIElement element in canvas.Children)
                    //{
                    //    canvas.Children.Remove(element);
                    //}
                    canvas.Children.Clear();
                    canvas = null;
                }

                Controls.Clear();
                Containers.Clear();

                this.IsVisibleChanged -= DataEntryCanvas_IsVisibleChanged;
            }
            // free native resources if there are any.
        }
        #endregion // IDisposable Methods

        public override void OnApplyTemplate()
        {
            this.IsVisibleChanged += DataEntryCanvas_IsVisibleChanged;
            base.OnApplyTemplate();

            if (this.DataContext is PageViewModel)
            {
                PageViewModel pageVM = (this.DataContext as PageViewModel);

                ProjectViewModel projectVM = pageVM.FormVM.ProjectVM;
                this.DataContext = projectVM;

                var canvas = this.GetTemplateChild("canvasMain") as Canvas;
                if (canvas != null)
                {
                    DragCanvas dragCanvas = ((DragCanvas)canvas);
                    //dragCanvas.Margin = new Thickness(35, 35, 35, 35);

                    DesignerControlFactory factory = new DesignerControlFactory();

                    Controls = factory.CreateControlsForPage(pageVM);
                    Containers = new Dictionary<UIElement, ElementDesignBorder>();

                    foreach (KeyValuePair<UIElement, IField> kvp in this.Controls)
                    {
                        if (kvp.Value.PageId.Equals(pageVM.Page.PageId))
                        {
                            kvp.Key.ClipToBounds = false;

                            ElementDesignBorder border = new ElementDesignBorder();
                            border.Background = Brushes.Transparent;
                            border.Content = kvp.Key;
                            border.ClipToBounds = false;
                            //border.Margin = new Thickness(-2, -2, -2, -2);

                            border.Drag += element_Drag;
                            border.DragStart += element_DragStart;
                            border.DragStop += element_DragStop;

                            //DraggableElementDecorator decorator = new DraggableElementDecorator();
                            //decorator.ClipToBounds = false;
                            //decorator.Child = border;
                            //decorator.Drag += element_Drag;
                            //decorator.DragStart += element_DragStart;
                            //decorator.DragStop += element_DragStop;

                            Containers.Add(kvp.Key, border);

                            dragCanvas.Children.Add(border);

                            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(border);

                            if (kvp.Key is TextBox || kvp.Key is ComboBox || kvp.Key is DatePicker)
                            {
                                Adorner adorner = new Adorners.ResizingAdorner(border, Adorners.Handlebars.TwoWayLeftRight);
                                adorner.Visibility = System.Windows.Visibility.Hidden;
                                adornerLayer.Add(adorner);
                            }
                            else if ((kvp.Key is Image && kvp.Value is StaticImageField) || kvp.Key is CheckBox)
                            {
                                Adorner adorner = new Adorners.ResizingAdorner(border, Adorners.Handlebars.None);
                                adorner.Visibility = System.Windows.Visibility.Hidden;
                                adornerLayer.Add(adorner);
                            }
                            else
                            {
                                Adorner adorner = new Adorners.ResizingAdorner(border, Adorners.Handlebars.EightWay);
                                adorner.Visibility = System.Windows.Visibility.Hidden;
                                adornerLayer.Add(adorner);
                            }

                            //adornerLayer.DataContext = border;

                            //Binding visBinding = new Binding("IsSelected");
                            //visBinding.Mode = BindingMode.OneWay;
                            //visBinding.Converter = this.TryFindResource("BooleanToVisibilityConverter") as BooleanToVisibilityConverter;
                            //adornerLayer.SetBinding(AdornerLayer.VisibilityProperty, visBinding);

                            string widthBindingString = String.Empty;
                            string heightBindingString = String.Empty;
                            string topBindingString = String.Empty;
                            string leftBindingString = String.Empty;

                            if (kvp.Key is Button)
                            {
                                widthBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptWidth";
                                heightBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptHeight";
                                topBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptTop";
                                leftBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptLeft";
                            }
                            else if (kvp.Key is Rectangle)
                            {
                                widthBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].Width";
                                heightBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].Height";
                                topBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].InputTop";
                                leftBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].InputLeft";
                            }
                            else if (!(kvp.Key is Label))
                            {
                                widthBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].InputWidth";
                                heightBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].InputHeight";
                                topBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].InputTop";
                                leftBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].InputLeft";
                            }
                            else
                            {
                                widthBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptWidth";
                                heightBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptHeight";
                                topBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptTop";
                                leftBindingString = "SelectedForm.Fields[" + kvp.Value.Name + "].PromptLeft";
                            }

                            Binding widthBinding = new Binding(widthBindingString);
                            Binding heightBinding = new Binding(heightBindingString);

                            widthBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                            heightBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

                            widthBinding.Mode = BindingMode.TwoWay;
                            heightBinding.Mode = BindingMode.TwoWay;

                            //if (kvp.Key is Rectangle || kvp.Key is ImagePicker)
                            //{
                            //    widthBinding.Converter = TryFindResource("DoublePlusFourConverter") as IValueConverter;
                            //    heightBinding.Converter = TryFindResource("DoublePlusFourConverter") as IValueConverter;
                            //}

                            Binding leftBinding = new Binding(leftBindingString);
                            Binding topBinding = new Binding(topBindingString);

                            leftBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                            topBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

                            leftBinding.Mode = BindingMode.TwoWay;
                            topBinding.Mode = BindingMode.TwoWay;

                            border.SetBinding(Canvas.LeftProperty, leftBinding);
                            border.SetBinding(Canvas.TopProperty, topBinding);

                            if (!(kvp.Key is CheckBox))
                            {
                                border.SetBinding(AdornerDecorator.WidthProperty, widthBinding);
                                border.SetBinding(AdornerDecorator.HeightProperty, heightBinding);
                            }

                            kvp.Key.Focusable = false;

                            //if (kvp.Value is DataField && kvp.Value is IInputField && !(kvp.Key is Label))
                            //{
                            //    AdornerLayer layer = AdornerLayer.GetAdornerLayer(kvp.Key);

                            //    TextBlock info = new TextBlock();
                            //    info.Padding = new Thickness(4);
                            //    info.Background = Brushes.Black;
                            //    info.Foreground = Brushes.White;
                            //    info.Text = kvp.Value.TabIndex.ToString();

                            //    Binding visBinding = new Binding("IsShowingTabIndices");
                            //    visBinding.Mode = BindingMode.OneWay;
                            //    visBinding.Converter = this.TryFindResource("BooleanToVisibilityConverter") as BooleanToVisibilityConverter;

                            //    layer.SetBinding(AdornerLayer.VisibilityProperty, visBinding);

                            //    layer.Add(new Adorners.TextBlockAdorner(kvp.Key, info));
                            //}
                        }
                    }
                }
            }
            else
            {

            }
        }

        #endregion // Methods

        #region Event Handlers
        void DataEntryCanvas_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var canvas = this.GetTemplateChild("canvasMain") as DragCanvas;
            if (canvas != null)
            {
                DragCanvas dragCanvas = canvas as DragCanvas;
                foreach (FrameworkElement child in dragCanvas.Children)
                {
                    if (child is ElementDesignBorder)
                    {
                        ElementDesignBorder border = child as ElementDesignBorder;
                        border.Drag -= element_Drag;
                        border.DragStart -= element_DragStart;
                        border.DragStop -= element_DragStop;
                    }
                }

                if (Controls != null)
                {
                    Controls.Clear();
                }

                if (Containers != null)
                {
                    Containers.Clear();
                }

                canvas = null;
            }

            this.Dispose();
            GC.Collect();
        }

        void element_DragStop(object sender, EventArgs e)
        {
            DragTop = Canvas.GetTop(sender as FrameworkElement);
            DragLeft = Canvas.GetLeft(sender as FrameworkElement);
        }

        void element_DragStart(object sender, EventArgs e)
        {
            DragTop = Canvas.GetTop(sender as FrameworkElement);
            DragLeft = Canvas.GetLeft(sender as FrameworkElement);
        }

        private IField GetAssociatedField(UIElement element)
        {
            foreach (KeyValuePair<UIElement, IField> kvp in Controls)
            {
                if (element == kvp.Key)
                {
                    return kvp.Value;
                }
            }

            return null;
        }

        private void element_Drag(object sender, EventArgs e)
        {
            List<ElementDesignBorder> decoratorsToMove = new List<ElementDesignBorder>();

            if (sender is ElementDesignBorder)
            {
                ElementDesignBorder decorator = sender as ElementDesignBorder;
                FrameworkElement element = decorator.GetContainedElement();

                if (element is Label)
                {
                    IField field = GetAssociatedField(element);

                    foreach (KeyValuePair<UIElement, IField> kvp in Controls)
                    {
                        if (kvp.Key != element && kvp.Value == field)
                        {
                            ElementDesignBorder decoratorToMove = Containers[kvp.Key];
                            decoratorsToMove.Add(decoratorToMove);
                        }
                    }

                    foreach (ElementDesignBorder decoratorToMove in decoratorsToMove)
                    {
                        double topOffset = Canvas.GetTop(decorator) - DragTop;
                        double leftOffset = Canvas.GetLeft(decorator) - DragLeft;

                        IField associatedFieldToMove = GetAssociatedField(decoratorToMove.GetContainedElement());

                        Canvas.SetTop(decoratorToMove, (Canvas.GetTop(decoratorToMove) + topOffset));
                        Canvas.SetLeft(decoratorToMove, (Canvas.GetLeft(decoratorToMove) + leftOffset));
                    }
                }

                DragTop = Canvas.GetTop(decorator);
                DragLeft = Canvas.GetLeft(decorator);
            }
        }
        #endregion // Event Handlers
    }
}
